package com.mgq.juc.design.guard;

import lombok.extern.slf4j.Slf4j;

/**
 * @author MaGuangQi
 * 用在一个线程等待另外一个线程的执行结果
 * @date 2021-12-16 09:00
 **/
@Slf4j
public class TestGuardedSuspension {
    public static void main(String[] args) {
        Guarded guarded = new Guarded();
        Thread t1 = new Thread(() -> {
            Object o = guarded.get(2000);
            log.debug("结果是:{}", o);
        }, "t1");

        Thread t2 = new Thread(() -> {
            try {
                //模拟任务执行
                log.debug("开始执行任务");
                Thread.sleep(3000);
                log.debug("任务执行结束");
                guarded.complete(new Object());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2");

        t1.start();
        t2.start();
    }
}

class Guarded {
    private Object response;

    public Object get() {
        synchronized (this) {
            while (response == null) {
                //一直等待 结果
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        return response;
    }

    /**
     * 带超时的get()方法
     * 处理完时间-开始时间 >=timeout时,就应该返回
     *
     * @param timeout
     * @return
     */
    public Object get(long timeout) {
        synchronized (this) {
            long startTime = System.currentTimeMillis();
            //记录经过多长时间

            long passedTime = 0;
            while (response == null) {
                //达到时间了,直接终止
                long waitTime = timeout - passedTime;
                if (waitTime <= 0) {
                    break;
                }
                try {
                    //应该等多久?
                    //如果是timeout,不准确,
                    //应该是等待 剩余时间

                    //假设等待2秒
                    //开始时间 15:00:00
                    //虚假唤醒 15:00:01
                    //此时passedTime=1秒.
                    //此时应该只需要再等待1秒,因为已经经历了1秒
                    this.wait(waitTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                long endTime = System.currentTimeMillis();
                passedTime = endTime - startTime;
            }

        }
        return response;
    }

    public void complete(Object response) {
        this.response = response;
        synchronized (this) {
            //通知线程
            this.notifyAll();
        }
    }
}
